package com.ctosb.classviewer.clazz.attr;

import com.ctosb.classviewer.ClassReader;
import com.ctosb.classviewer.clazz.AbstractParse;
import com.ctosb.classviewer.clazz.Parseable;
import com.ctosb.classviewer.clazz.attr.frame.*;
import lombok.Data;
import lombok.experimental.Accessors;

import java.util.HashMap;
import java.util.Map;

/**
 *
 */
@Data
@Accessors(chain = true)
public class Stack_map_frame extends AbstractParse {

    private BaseFrame frame;
//    private Same_frame same_frame;
//    private Same_locals_1_stack_item_frame same_locals_1_stack_item_frame;
//    private Same_locals_1_stack_item_frame_extended same_locals_1_stack_item_frame_extended;
//    private Chop_frame chop_frame;
//    private Same_frame_extended same_frame_extended;
//    private Append_frame append_frame;
//    private Full_frame full_frame;

    private static Map<Range, Class<? extends BaseFrame>> map = new HashMap<>();

    static {
        map.put(new Range((short) 0, (short) 63), Same_frame.class);
        map.put(new Range((short) 64, (short) 127), Same_locals_1_stack_item_frame.class);
        map.put(new Range((short) 247, (short) 247), Same_locals_1_stack_item_frame_extended.class);
        map.put(new Range((short) 248, (short) 250), Chop_frame.class);
        map.put(new Range((short) 251, (short) 251), Same_frame_extended.class);
        map.put(new Range((short) 252, (short) 254), Append_frame.class);
    }

    @Override
    public Stack_map_frame parse(ClassReader classReader) {
        byte type = classReader.readByte();
        int unsignType = type & 0xff;
        Class<? extends BaseFrame> clazz = map.entrySet()
                .stream()
                .filter(entry -> entry.getKey().contains(unsignType))
                .findFirst()
                .orElseThrow(() -> {
                    return new UnsupportedOperationException("un support frame type " + unsignType);
                })
                .getValue();
        try {
            frame = clazz.newInstance()
                    .setFrame_type(type)
                    .parse(classReader, getClassFile());
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("parse frame exception.", e);
        }
        return this;
    }

    public static class Range {

        short min;
        short max;

        public Range(short min, short max) {
            this.min = min;
            this.max = max;
        }

        public boolean contains(int unsignType) {
            return min <= unsignType && unsignType <= max;
        }
    }

}
